作业, 识别图像数字

本节课为「计算机视觉 CV 核心知识」第 13 节;

「AI秘籍」系列课程:

可直接在橱窗里购买,或者到文末领取优惠后购买:

茶桁的AI秘籍CV_13

Hi, 大家好。我是茶桁。

前几节课我们是讲了图像处理和计算机视觉。我们认识了计算机视觉的如何去提特征,学了图像处理。然后用图像处理的方法对图像提了特征。我们上节课学了提特征了,学了提 hog 特征,之后 LBP 特征和哈尔特征遗留下来了,我们这节课把它补充上去。

在这之前,我们还是先来看一下上周的作业。不知道上周有多少人去认真完成作业了,完成的效果怎么样。

之前我讲作业写在了 jupyter notebook 里,这节课咱们也继续用这个格式来讲。这种格式其实对讲课和一些实验还是比较友好一些。

作业最前面,咱们还是引入 torch,并且生成了 10 张图片的矩阵。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import torch
def generate_data():
# 本函数生成0-9,10个数字的图片矩阵
image_data=[]
num_0 = torch.tensor(
[[0,0,1,1,0,0],
[0,1,0,0,1,0],
[0,1,0,0,1,0],
[0,1,0,0,1,0],
[0,0,1,1,0,0],
[0,0,0,0,0,0]])
image_data.append(num_0)
num_1 = torch.tensor(
[[0,0,0,1,0,0],
[0,0,1,1,0,0],
[0,0,0,1,0,0],
[0,0,0,1,0,0],
[0,0,1,1,1,0],
[0,0,0,0,0,0]])
image_data.append(num_1)
num_2 = torch.tensor(
[[0,0,1,1,0,0],
[0,1,0,0,1,0],
[0,0,0,1,0,0],
[0,0,1,0,0,0],
[0,1,1,1,1,0],
[0,0,0,0,0,0]])
image_data.append(num_2)
num_3 = torch.tensor(
[[0,0,1,1,0,0],
[0,0,0,0,1,0],
[0,0,1,1,0,0],
[0,0,0,0,1,0],
[0,0,1,1,0,0],
[0,0,0,0,0,0]])
image_data.append(num_3)
num_4 = torch.tensor(
[
[0,0,0,0,1,0],
[0,0,0,1,1,0],
[0,0,1,0,1,0],
[0,1,1,1,1,1],
[0,0,0,0,1,0],
[0,0,0,0,0,0]])
image_data.append(num_4)
num_5 = torch.tensor(
[
[0,1,1,1,0,0],
[0,1,0,0,0,0],
[0,1,1,1,0,0],
[0,0,0,0,1,0],
[0,1,1,1,0,0],
[0,0,0,0,0,0]])
image_data.append(num_5)
num_6 = torch.tensor(
[[0,0,1,1,0,0],
[0,1,0,0,0,0],
[0,1,1,1,0,0],
[0,1,0,0,1,0],
[0,0,1,1,0,0],
[0,0,0,0,0,0]])
image_data.append(num_6)
num_7 = torch.tensor(
[
[0,1,1,1,1,0],
[0,0,0,0,1,0],
[0,0,0,1,0,0],
[0,0,0,1,0,0],
[0,0,0,1,0,0],
[0,0,0,0,0,0]])
image_data.append(num_7)
num_8 = torch.tensor(
[[0,0,1,1,0,0],
[0,1,0,0,1,0],
[0,0,1,1,0,0],
[0,1,0,0,1,0],
[0,0,1,1,0,0],
[0,0,0,0,0,0]])
image_data.append(num_8)
num_9 = torch.tensor(
[[0,0,1,1,1,0],
[0,1,0,0,1,0],
[0,0,1,1,1,0],
[0,1,0,0,1,0],
[0,0,0,0,1,0],
[0,0,0,0,0,0]])
image_data.append(num_9)
return image_data

我们定义了一个生成函数 generate_data,这个函数可以生成 10 张不通的图片。然后咱们将其 append 在一个变量 image_data 里并将其 return 出来,这样咱们的 image_data 就可以访问这 10 张图片了。

我们在外面定义了一个 img,用于接收函数返回出来的 image_data,并且定义一个函数讲图片打印出来:

1
2
3
4
import matplotlib.pyplot as plt
img = generate_data()
for i in range(len(img)):
plt.imshow(img[i], cmap='gray')

20240720121947

注意,我这里没有加 plt.show(),所以图片最终只会打印最后一个矩阵。因为画布就这一张,前面的图片数据都被覆盖了。如果我加了 plt.show(),则会展示出 10 张画布,这样十张图片就都被打印了出来。

所以最终,咱们展示了 9 这个数字的图片。

接着,咱们还是打印一下单独的数据,看看数据格式和数据本身是否正确。

1
2
3
4
5
6
7
8
9
10
x = img[8]
print(x)

---
tensor([[0, 0, 1, 1, 0, 0],
[0, 1, 0, 0, 1, 0],
[0, 0, 1, 1, 0, 0],
[0, 1, 0, 0, 1, 0],
[0, 0, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0]])

OK,数据我们拿到了,也把数据的矩阵打印出来了。 从矩阵上 1 的形状上来看,数据是没有问题。

下面,我们就可以开始对这个图片提特征了。

在布置作业的时候我给大家定义了一个提特征的函数 get_feature,我们就可以在这个函数中直接写一个提特征的操作。

咱们直接写一句 return torch.sum(x, 0) + torch.sum(x, 1),我这取的是一个列投影和行投影之和。

1
2
def get_feature(x):
return torch.sum(x, 0) + torch.sum(x, 1)

这一步之后,咱们这一个特征函数就完成了。这个特征其实可以做的很简单,就像我这样。也可以做的复杂一些,比如说你提个 hog,hog 嘛就是一个特定核的一个 feature rd,出来之后你再提一下直方图就可以了。

拿到特征之后,我们后面就要想办法对特征进行判断了。我们这里先观察下我们这个特征。要观察特征,就取一个图片出来观察。我们就把 0 这个图片拿出来观察。

1
2
3
4
5
6
7
8
9
10
11
12
13
x = img[0]

print(torch.sum(x, 1))
print(x)

---
tensor([2, 2, 2, 2, 2, 0])
tensor([[0, 0, 1, 1, 0, 0],
[0, 1, 0, 0, 1, 0],
[0, 1, 0, 0, 1, 0],
[0, 1, 0, 0, 1, 0],
[0, 0, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0]])

我们把它的行投影特征打印出来,我们再把 x 本身打印出来。我们看一下行通用特征到底做了什么事情。

首先,咱们 打印出来的第一个 tensor 是 0 的行投影,第二个 tensor 矩阵是这整张图片的一个矩阵。

行投影是什么意思呢?我们来看 x 矩阵的第一行: [0, 0, 1, 1, 0, 0],一共是 2 个 1,那行投影的第一个数字就是 2,第二行也是如此,之后每一行都是如此,一直到最后一行都是 0,没有一个 1,所以行投影矩阵的最后一个数字是 0。

那列投影是多少呢?我们盲猜一下:

1
2
3
4
5
6
tensor([[0, 0, 1, 1, 0, 0],
[0, 1, 0, 0, 1, 0],
[0, 1, 0, 0, 1, 0],
[0, 1, 0, 0, 1, 0],
[0, 0, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0]])

第一列,0 个 1,是 0,第二列,3,第三列,2,第四列,2,第五列,3,第六列,0。那应该是 [0,3,2,2,3,0], 然后咱们打印一下看看对不对。

1
2
3
4
print(torch.sum(x, 0))

---
tensor([0, 3, 2, 2, 3, 0])

果然如此对吧?

这就是我提特征的方式。那么这里在 torch.sum(x, 0) 这个操作中,0 就指的是第一个维度,第一个维度通常是列这个维度。第二个维度是行这个维度。

OK,我们就拿到这个 feature。行列相加,0 这个图片最后提取出来的特征就应该是 [2, 2, 2, 2, 2, 0] + [0, 3, 2, 2, 3, 0] = [2, 5, 4, 4, 5, 0]。可以接着验证一下:

1
2
3
4
print(get_feature(x))

---
tensor([2, 5, 4, 4, 5, 0])

应该是对的。

那么这就是我们对 0 这个数字提的特征。验证了特征提取之后,我们可以直接拿函数来获取其他数字的特征,我们再看看 6 这个数字提的特征。

1
2
3
4
print(get_feature(img[6]))

---
tensor([2, 4, 6, 5, 3, 0])

通过提特征,我们就把原来的矩阵用一个向量来表示它了,原来是 6*6 的矩阵,现在就是 1*6 的向量来表示。

那么提到特征之后,后边我们就要对它进行判断了。

1
2
3
4
5
6
7
8
9
10
def model(x, img):
y=-1
for i in range(0, 10):
diff_feature = get_feature(x) - get_feature(img[i])
if torch.sum(torch.abs(diff_feature)) == 0:
y = i
break
print('输出 x 为:\n {}, \n 输出 x 的类别为 {}'.format(x, y))

return y

我们看一下函数 model 里面如何对它进行判断的。提到特征之后,对它进行直接判断,直接对比了。

get_feature(x) - get_feature(img[i]),img 就作为模型的参数作为对比的模板了。然后我们要识别的图片是 x,我们这个 x 提完特征和我们的模板特征相减,相减之后我们判断一下是不是完全相等。

相减之后这个向量再取个绝对值,再 sum 一下是不是等于 0,如果等于 0,就认为它们俩完全相等。当前循环到 i,那当前 x 就是第 i 个数字。

所以如果完全相等了,y 等于 i, 然后 break 跳出循环,再打印出来。

1
2
3
4
5
6
7
8
9
10
11
model(img[2], img)

---
输出 x 为:
tensor([[0, 0, 1, 1, 0, 0],
[0, 1, 0, 0, 1, 0],
[0, 0, 0, 1, 0, 0],
[0, 0, 1, 0, 0, 0],
[0, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0]]),
输出 x 的类别为 2

我给的这个例子是非常简单的,其实这里可以用线性模型。咱们之后在一些企业项目实战中会讲解更多的模型。这里只不过是告诉大家有这么一个决策的过程。

所以说这个作业你只要完成就可以了,只要有一个决策的过程就可以了,对性能我们不做要求。

在获取特征的时候,如果我们不是拿行投影和列投影去计算,只获取行投影,那可能 8 就会被识别为 2。也就是特征并不完全适用。

这就是说,我们去做决策的时候,去设计传统的机器学习模型的时候,我们这个特征是需要手动去调整的。这还是一个非常简单的任务,随便一调整就可以百分百了。那如果说我们数据比较复杂,我们如果做一个模型想让它性能比较好的话,通常我们需要调特征,调特征是非常麻烦的事情。

比如说我这里取个行投影不行,然后我行投影加上列投影就可以了。那如果行投影加上列投影不行我还得试别的特征。hog、LBP等等,都得来回的调,非常的麻烦。

OK,这是我们上节课的一个作业,下来大家还可以尝试更多的提取特征的方法或者模型,多尝试一下。

下节课咱们就开始进入另外一个部分《GPU 如何在 LeNet 提升中发挥作用》。

好,咱们下节课见,拜拜。

作者

Hivan Du

发布于

2024-08-25

更新于

2024-07-29

许可协议

评论